#include "Ghost.h"

Ghost::Ghost(void)
: x(0)
{
}

Ghost::~Ghost(void)
{
}

HRESULT Ghost::Init(int i,vector <vector<int> > * nmap,bool nPlayerControlled, CZenKeyboard *Keyboard)
{
	m_Keyboard = Keyboard;

	height=rolle=0;
	seePacmanDirectly=false;
	addition =true;
	whatIsUnder=5;
	centerOfField=true;
	direction=STOP;
	nearbyGhosts=0;
	playerControlled=nPlayerControlled;
	string line;
	string ghostLocation = "res/ghost";
	ostringstream oss;
	oss << ghostLocation << i;
	ghostLocation = oss.str();
	ghostLocation += ".x";
	int z = ghostLocation.length();
	char*  gh = new char[z+1];
	for(int a=0;a<ghostLocation.length();a++)
		{
		gh[a]=ghostLocation[a];
		}
	gh[z]='\0';
	m_pGhostMesh.LoadXFile(gh);
	AddObject(&m_pGhostMesh);
	map=nmap;
	
	return S_OK;
}

void Ghost::SetPosition(float x, float y,float z)
{
	CZenFrame::SetPosition(x, y, z);
	this->x=int(x+0.5);
	this->z=int(z+0.5);
	startX=x;
	startZ=z;
}
void Ghost::Reset()//resets ghost after being killed by pacman
{	
	(*map)[z][x]=whatIsUnder;
	CZenFrame::SetPosition(startX,0.5,startZ);
	direction=STOP;
	x=int(startX+0.5);
	z=int(startZ+0.5);
	(*map)[z][x]=2;
	centerOfField=true;
	SetVelocity(0,0,0);
}
void Ghost::Move(int newDirection)
{
	direction=newDirection;
	if(height>=360)height=0;
	if(addition)
		rolle++;
	else 
		rolle--;
	if(rolle>=45)
		addition=false;
	if(rolle<=-45)
		addition=true;
	SetRoll(D3DXToRadian(rolle/2));
	CZenFrame::SetPosition(m_vPosition.x,(sin(D3DXToRadian(height))+1)/8,m_vPosition.z);
	height+=4;

	switch (direction)
	{
		case UP:
			SetYaw(D3DXToRadian(180));
			SetVelocity(0,0,GHOST_SPEED);
			break;
		case DOWN:		
			SetYaw(D3DXToRadian(0));
			SetVelocity(0,0,-GHOST_SPEED);
			break;
		case LEFT:
			SetYaw(D3DXToRadian(90));
			SetVelocity(-GHOST_SPEED,0,0);
			break;
		case RIGHT:
			SetYaw(D3DXToRadian(-90));
			SetVelocity(GHOST_SPEED,0,0);
			break;
		default:
			SetVelocity(0,0,0);
			break;
	}
}


void Ghost::UpdateMapPosition()
{
	float fx,fy,fz;
	
	GetPosition(fx,fy,fz);
	
	if(fx-x>1)//one field right
	{	
		centerOfField=true;
		if(whatIsUnder!=2)
			(*map)[z][x]=whatIsUnder;//put on the map the thing that was here before ghost
		x++;//update internal ghost position;		
		if((*map)[z][x]!=3)
			whatIsUnder=(*map)[z][x];//remember what is here on the map
		else
			whatIsUnder=5;//special case if that is pacman
		(*map)[z][x]=2;//update ghost position on map		
	}
	else if(fx-x<-1)//one field left
	{
		centerOfField=true;
		if(whatIsUnder!=2)
			(*map)[z][x]=whatIsUnder;//put on the map the thing that was here before ghost
		x--;//update internal ghost position;
		if((*map)[z][x]!=3)
			whatIsUnder=(*map)[z][x];//remember what is here on the map
		else
			whatIsUnder=5;//special case if that is pacman
		(*map)[z][x]=2;//update ghost position on map
	}
	else if(fz-z>1)//one field up
	{
		centerOfField=true;
		if(whatIsUnder!=2)
			(*map)[z][x]=whatIsUnder;//put on the map the thing that was here before ghost		
		z++;//update internal ghost position;
		if((*map)[z][x]!=3)
			whatIsUnder=(*map)[z][x];//remember what is here on the map
		else
			whatIsUnder=5;//special case if that is pacman
		(*map)[z][x]=2;//update ghost position on map
	}
	else if(fz-z<-1)//one field down
	{
		centerOfField=true;
		if(whatIsUnder!=2)
			(*map)[z][x]=whatIsUnder;//put on the map the thing that was here before ghost
		z--;//update internal ghost position;
		if((*map)[z][x]!=3)
			whatIsUnder=(*map)[z][x];//remember what is here on the map
		else
			whatIsUnder=5;//special case if that is pacman
		(*map)[z][x]=2;//update ghost position on map
	}
	else if(STOP!=direction)
		centerOfField=false;
}

void Ghost::ThinkAndMove(bool retreatMode,int pacmanX,int pacmanZ)
{	
	UpdateMapPosition();
	if(playerControlled )
	{
		if(m_Keyboard->IsKeyDown(DIK_W) ) 
		{			
			if(!((*map)[z+1][x]==1) && centerOfField)
			{
				direction = UP;
			}
		}
		if(m_Keyboard->IsKeyDown(DIK_A))
		{			
			if(!((*map)[z][x-1]==1)  && centerOfField)
			{
				direction = LEFT;
			}
		}
		if(m_Keyboard->IsKeyDown(DIK_S) )
		{			
			if(!((*map)[z-1][x]==1) && centerOfField )
			{
				direction = DOWN;
			}
		}
		if(m_Keyboard->IsKeyDown(DIK_D) )
		{			
			if(!((*map)[z][x+1]==1) && centerOfField )
			{
				direction = RIGHT;
			}
		}	
	}
	else if(centerOfField)//AI controlled
	{
		nearbyGhosts=0;
		seePacmanDirectly=false;
		seePacmanAroundCorner=false;
		for(int i=1;i<5;i++)
			directionScores[i]=0;
		directionScores[direction]+=PREVIOUS_DIRECTION_SCORE;

		//////try to make a lot of turns when do not see pacman
		if(direction>=3)
		{
			directionScores[UP]+=TAKE_TURN_SCORE;
			directionScores[DOWN]+=TAKE_TURN_SCORE;
		}
		else
		{
			directionScores[LEFT]+=TAKE_TURN_SCORE;
			directionScores[RIGHT]+=TAKE_TURN_SCORE;
		}
		//////

		if(retreatMode)
		{
			if(pacmanX>x)
				directionScores[RIGHT]-=PACMAN_POSITION_SCORE;
			if(pacmanX<x)
				directionScores[LEFT]-=PACMAN_POSITION_SCORE;
			if(pacmanZ>z)
				directionScores[UP]-=PACMAN_POSITION_SCORE;
			if(pacmanZ<z)
				directionScores[DOWN]-=PACMAN_POSITION_SCORE;
		}
		else
		{
			if(pacmanX>x)
				directionScores[RIGHT]+=PACMAN_POSITION_SCORE;
			if(pacmanX<x)
				directionScores[LEFT]+=PACMAN_POSITION_SCORE;
			if(pacmanZ>z)
				directionScores[UP]+=PACMAN_POSITION_SCORE;
			if(pacmanZ<z)
				directionScores[DOWN]+=PACMAN_POSITION_SCORE;
		}
		
		
		//PHASE 1:
		//LOOK FOR PACMAN
		
				
		//LOOK TO THE RIGHT
		if((*map)[z][x+1]==1)
			directionScores[RIGHT]=-1000000;
		for(int i=x+1; (*map)[z][i]!=1 && !seePacmanDirectly; i++)
		{
			if((*map)[z][i]==0)
				directionScores[RIGHT]+=BALL_SCORE;
			if((*map)[z][i]==3)//see pacman
			{
				if(!(GHOST_RIGHT & nearbyGhosts))
				{
					if(retreatMode)
						FindEscapeDirection(RIGHT);
					else
						direction=RIGHT;

					seePacmanDirectly=true;
				}
			}
			else if((*map)[z][i]==2)//see other ghost
			{
				nearbyGhosts|=GHOST_RIGHT;
				directionScores[RIGHT]+=GHOST_DIRECTLY_SCORE;
			}
			LookAroundCornerHor(i,RIGHT,retreatMode);
		}
		//LOOK TO THE LEFT
		if((*map)[z][x-1]==1)
			directionScores[LEFT]=-1000000;
		for(int i=x-1; (*map)[z][i]!=1 && !seePacmanDirectly; i--)
		{
			if((*map)[z][i]==0)
				directionScores[LEFT]+=BALL_SCORE;
			else if((*map)[z][i]==3)//see pacman
			{
				if(!(GHOST_LEFT & nearbyGhosts))
				{
					if(retreatMode)
						FindEscapeDirection(LEFT);
					else
						direction=LEFT;

					seePacmanDirectly=true;
				}
			}
			else if((*map)[z][i]==2)//see other ghost
			{
				nearbyGhosts|=GHOST_LEFT;
				directionScores[LEFT]+=GHOST_DIRECTLY_SCORE;
			}
			LookAroundCornerHor(i,LEFT,retreatMode);
		}
		//LOOK BELOW
		if((*map)[z-1][x]==1)
			directionScores[DOWN]=-1000000;
		for(int k=z-1; (*map)[k][x]!=1 && !seePacmanDirectly; k--)
		{
			if((*map)[k][x]==0)
				directionScores[DOWN]+=BALL_SCORE;
			else if((*map)[k][x]==3)//see pacman
			{
				if(!(GHOST_BELOW & nearbyGhosts))
				{
					if(retreatMode)
						FindEscapeDirection(DOWN);
					else
						direction=DOWN;
					seePacmanDirectly=true;
				}
			}
			else if((*map)[k][x]==2)//see other ghost
			{
				nearbyGhosts|=GHOST_BELOW;
				directionScores[DOWN]+=GHOST_DIRECTLY_SCORE;
			}
			LookAroundCornerVer(k,DOWN,retreatMode);
		}
		//LOOK ABOVE
		if((*map)[z+1][x]==1)
			directionScores[UP]=-1000000;
		for(int k=z+1; (*map)[k][x]!=1 && !seePacmanDirectly; k++)
		{
			if((*map)[k][x]==0)
				directionScores[UP]+=BALL_SCORE;
			else if((*map)[k][x]==3)//see pacman
			{
				if(!(GHOST_ABOVE & nearbyGhosts))
				{
					if(retreatMode)
						FindEscapeDirection(UP);
					else
						direction=UP;
					seePacmanDirectly=true;
				}
			}
			else if((*map)[k][x]==2)//see other ghost
			{
				nearbyGhosts|=GHOST_ABOVE;
				directionScores[UP]+=GHOST_DIRECTLY_SCORE;
			}
			LookAroundCornerVer(k,UP,retreatMode);
		}
		
		//PHASE 2:
		//PACMAN NOT FOUND - WALK IN DIRECTION OF HIGHEST SCORE
		if(!seePacmanDirectly && !seePacmanAroundCorner)
		{
			for(int i=1;i<5;i++)//make each score different
			directionScores[i]+=30*rand()/(RAND_MAX+1.0);

			direction=UP;//#define UP 1 in ZenFrame.h

			double highestScore=directionScores[1];
			for(int i=2;i<5;i++)
				if(directionScores[i]>directionScores[direction])
					direction=i;
		}
	}
	//things done always, both for player controlled and normal ghosts
	CollidesWithWallOrGhost();	
	this->Move(direction);	
	if(direction==STOP)
		centerOfField=true;
}

void Ghost::FindEscapeDirection(int wherePacmanIs)
{
	direction=1+rand()%4;
	while(direction==wherePacmanIs)
		direction=1+rand()%4;
}

void Ghost::LookAroundCornerVer(int k,int dir,bool retreatMode)
{	
	for(int i=x+1;(*map)[k][i]!=1;i++)//look around a corner if there is any
	{
		if((*map)[k][i]==0)
			directionScores[dir]+=BALL_SCORE;
		else if((*map)[k][i]==2)
			directionScores[dir]+=GHOST_AROUND_CORNER_SCORE;
		else if((*map)[k][i]==3)//see pacman
		{
			seePacmanAroundCorner=true;
			if(retreatMode)
				FindEscapeDirection(dir);
			else
			{
				if(!(GHOST_BELOW & nearbyGhosts) && DOWN==dir)
					direction=DOWN;
				if(!(GHOST_ABOVE & nearbyGhosts) && UP==dir)
					direction=UP;
			}
			return ;
		}
	}
	for(int i=x-1;(*map)[k][i]!=1;i--)//look around other possible corner
	{
		if((*map)[k][i]==0)
			directionScores[dir]+=BALL_SCORE;
		else if((*map)[k][i]==2)
			directionScores[dir]+=GHOST_AROUND_CORNER_SCORE;
		if((*map)[k][i]==3)//see pacman
		{
			seePacmanAroundCorner=true;
			if(retreatMode)
				FindEscapeDirection(dir);
			else
			{
				if(!(GHOST_BELOW & nearbyGhosts) && DOWN==dir)
					direction=DOWN;
				if(!(GHOST_ABOVE & nearbyGhosts) && UP==dir)
					direction=UP;
			}
			return;
		}
	}
}
void Ghost::LookAroundCornerHor(int i,int dir,bool retreatMode)
{
	for(int k=z+1;(*map)[k][i]!=1;k++)//look around a corner if there is any
	{
		if((*map)[k][i]==0)
			directionScores[dir]+=BALL_SCORE;
		else if((*map)[k][i]==2)
			directionScores[dir]+=GHOST_AROUND_CORNER_SCORE;
		else if((*map)[k][i]==3)//see pacman
		{
			seePacmanAroundCorner=true;
			if(retreatMode)
				FindEscapeDirection(dir);
			else
			{
				if(!(GHOST_RIGHT & nearbyGhosts) && RIGHT==dir)
					direction=RIGHT;
				if(!(GHOST_LEFT & nearbyGhosts) && LEFT==dir)
					direction=LEFT;
			}
			return;
		}
	}
	for(int k=z-1;(*map)[k][i]!=1;k--)//look around other possible corner
	{
		if((*map)[k][i]==0)
			directionScores[dir]+=BALL_SCORE;
		else if((*map)[k][i]==2)
			directionScores[dir]+=GHOST_AROUND_CORNER_SCORE;
		else if((*map)[k][i]==3)//see pacman
		{
			seePacmanAroundCorner=true;
			if(retreatMode)
				FindEscapeDirection(dir);
			else
			{
				if(!(GHOST_RIGHT & nearbyGhosts) && RIGHT==dir)
					direction=RIGHT;
				if(!(GHOST_LEFT & nearbyGhosts) && LEFT==dir)
					direction=LEFT;
			}
			return;
		}
	}
}

void Ghost::CollidesWithWallOrGhost()
{
	switch (direction){

		case UP:
			if((*map)[z+1][x]==1 || (*map)[z+1][x]==2 && !playerControlled)
				direction=STOP;
			break;
		case DOWN:		
			if((*map)[z-1][x]==1 || (*map)[z-1][x]==2 && !playerControlled)
				direction=STOP;
			break;
		case LEFT:
			if((*map)[z][x-1]==1 || (*map)[z][x-1]==2 && !playerControlled)
				direction=STOP;
			break;
		case RIGHT:
			if((*map)[z][x+1]==1 || (*map)[z][x+1]==2 && !playerControlled)
				direction=STOP;
			break;
		default:
			break;
	}
}